package com.hyt.it.ogt.ks.controller;

import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.common.apiInfor.DataType;
import com.hyt.common.apiInfor.ParamType;
import com.hyt.core.exception.RedisException;
import com.hyt.it.ogt.ks.config.ConfigManager;
import com.hyt.it.ogt.ks.enums.BizStatusCode;
import com.hyt.it.ogt.ks.enums.CandidateStatusCode;
import com.hyt.it.ogt.ks.enums.ExamConfigCode;
import com.hyt.it.ogt.ks.feign.KwClient;
import com.hyt.it.ogt.ks.feign.model.ExamDTO;
import com.hyt.it.ogt.ks.function.KsQueueHandleExecutor;
import com.hyt.it.ogt.ks.model.entity.CandidateProcess;
import com.hyt.it.ogt.ks.model.vo.AppPlayAddressVO;
import com.hyt.it.ogt.ks.model.vo.ExamConfigVO;
import com.hyt.it.ogt.ks.model.vo.UploadPhotoVO;
import com.hyt.it.ogt.ks.model.vo.ValidPhotoVO;
import com.hyt.it.ogt.ks.model.vo.monitor.request.CandidatePhotoUpdateVO;
import com.hyt.it.ogt.ks.model.vo.monitor.response.AppQRCodeVO;
import com.hyt.it.ogt.ks.model.vo.monitor.response.MonitorSignForPcVO;
import com.hyt.it.ogt.ks.service.ExamInfoCacheService;
import com.hyt.it.ogt.ks.service.ICandidateProcessService;
import com.hyt.it.ogt.ks.service.ILogService;
import com.hyt.it.ogt.ks.service.IPcMonitorService;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.util.Pair;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * userid即examcandidateid
 * @author 刘恒活
 * @since 2020/8/24 10:12
 */
@RestController
@RequestMapping("/ks/pc/monitor")
@Api(tags = "7.PC端视频监控接口", value = "PC端视频监控接口")
@ApiSort(7)
@Slf4j
public class PcMonitorController {
    @Resource
    private ConfigManager configManager;
    @Resource
    private ICandidateProcessService iCandidateProcessService;
    @Resource
    private KwClient kwClient;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private ExamInfoCacheService examInfoCacheService;
    @Resource
    private ILogService iLogService;
    @Resource
    private IPcMonitorService iPcMonitorService;

    @ApiOperation(value = "7.1.1 获取签名", notes = "20000:成功，42070101：异常", response = MonitorSignForPcVO.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "candidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(1)
    @GetMapping("getSign")
    public ApiResponse getSign(String examId, String candidateId) {
        try {
            CandidateProcess one = iCandidateProcessService.lambdaQuery()
                    .select(CandidateProcess::getExamStatus, CandidateProcess::getEndTime)
                    .eq(CandidateProcess::getExamId, examId)
                    .eq(CandidateProcess::getCandidateId, candidateId).last("limit 1").one();
            if (one != null &&
                    (one.getExamStatus().intValue() >= CandidateStatusCode.COMMIT.getCode().intValue() || one.getEndTime() != null)) {
                boolean flag = false;

                String repExamConfigRtn = kwClient.getExamConfig(examId);
                ApiResponse<JSONArray> resExamConfig = JSON.parseObject(repExamConfigRtn, ApiResponse.class);
                List<ExamConfigVO> examConfigList = JSON.parseArray(resExamConfig.getData().toJSONString(), ExamConfigVO.class);
                // 获取延迟播放是否配置
                Optional<ExamConfigVO> delayMonitorVideo = examConfigList.stream().filter(item ->
                        ExamConfigCode.EXAM_CONFIG_CODE_DELAY_MONITOR_VIDEO.getCode().equalsIgnoreCase(item.getConfigCode())
                ).findFirst();

                if (delayMonitorVideo.isPresent() && "true".equalsIgnoreCase(delayMonitorVideo.get().getConfigValue())) {
                    Optional<ExamConfigVO> delayMonitorVideoTime = examConfigList.stream().filter(item ->
                            ExamConfigCode.EXAM_CONFIG_CODE_DELAY_MONITOR_VIDEO_TIME.getCode().equalsIgnoreCase(item.getConfigCode())
                    ).findFirst();
                    Integer delaySeconds = null;
                    if (delayMonitorVideoTime.isPresent()) {
                        String configValue = delayMonitorVideoTime.get().getConfigValue();
                        if (StringUtils.isNotBlank(configValue) && NumberUtil.isNumber(configValue)) {
                            delaySeconds = NumberUtil.parseInt(configValue);
                        }
                    }
                    if (delaySeconds != null) {
                        // 考生交卷结束时间
                        flag = !LocalDateTime.now().isBefore(one.getEndTime().plusSeconds(delaySeconds));
                    }
                } else {
                    flag = true;
                }
                if (flag) {
                    return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).build();
                }
            }
            String json = kwClient.getSign(examId, candidateId, 0);
            ApiResponse<MonitorSignForPcVO> apiResponse = ApiResponse.ConvertRet(json, MonitorSignForPcVO.class);
            log.info("请求考务接口kwClient.getSign返回数据{}", json);
            if (apiResponse.getCode().intValue() == BizStatusCode.KW_SUCCESS_OK.getCode().intValue()) {
                return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).data(apiResponse.getData()).build();
            }
        } catch (Exception e) {
            log.error("获取签名报错:{}", e);
        }
        return ApiResponse.builder().code(BizStatusCode.VIDEO_MONITOR_GET_SIGN_INFO_ERROR_CODE.getCode()).build();
    }

    @ApiOperation(value = "7.1.2 获取人脸核身URL", notes = "状态码：16001:成功，16049：参数有误，16050：异常", response = String.class)
    @ApiOperationSort(2)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "url", value = "回调前端的地址", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "from", value = "pc:电脑端web，wb：支持移动端web，微信和app内调用，如果为空，则默认值为pc",
                    dataType = DataType.STRING, paramType = ParamType.QUERY, required = true)
    })
    @GetMapping("getFaceUrl")
    public Object getFaceUrl(@RequestParam String examId, @RequestParam String examCandidateId,
                             @RequestParam String url, @RequestParam(required = false) String from) {
        String json = kwClient.getFaceUrl(examId, examCandidateId, url, from);
        return ApiResponse.ConvertRet(json, Map.class);
    }

    @ApiOperation(value = "7.1.3 考生抓拍文件上传")
    @ApiOperationSort(3)
    @PostMapping("/uploadPhoto")
    public ApiResponse uploadPhoto(@RequestBody CandidatePhotoUpdateVO vo) throws RedisException {
        String json = kwClient.uploadPhoto(JSON.toJSONString(vo));
        ApiResponse<String> res = ApiResponse.ConvertRet(json, String.class);
        if(res.getCode().intValue() != BizStatusCode.KW_SUCCESS_OK.getCode().intValue()) {
        	return ApiResponse.ConvertRet(json, String.class);
        }
        boolean updateCandidateProcess = false;
        UploadPhotoVO uploadPhoto = new UploadPhotoVO();
        uploadPhoto.setStatus(vo.getAuthentication());
        if(vo.getAuthentication() == 0) {
	        String jsonFaceDetect = kwClient.faceDetectByUrl(vo.getExamId(), vo.getExamCandidateId(), res.getData());
	        log.info("请求考务人脸识别接口kwClient.faceDetectByUrl返回数据{}", jsonFaceDetect);
	        ApiResponse<Integer> resjsonFaceDetect = ApiResponse.ConvertRet(jsonFaceDetect, Integer.class);
	        uploadPhoto.setPersonCount(resjsonFaceDetect.getData());
	        updateCandidateProcess = true;
        } else {
        	Boolean enabled = configManager.getEnabled().equals("true") ? true : false;
        	String candidates = configManager.getCandidateIds();
        	Boolean configEnabled = enabled && candidates.contains(vo.getExamCandidateId());
        	Boolean redisEnabled = redisTemplate.opsForValue().get("CANCEL_FACE_VAILD_" + vo.getExamCandidateId()) != null;
        	if(configEnabled || redisEnabled) {
        		log.info("港澳台考生{}跳过人脸比对", vo.getExamCandidateId());
        		uploadPhoto.setFaceCompareResult(1);
        		updateCandidateProcess = true;
        	} else {
        		String bm = kwClient.faceDetectEnrollPhotoByUrl(vo.getExamId(),
                        vo.getExamCandidateId(), res.getData());
            	ApiResponse<Integer> resjsonFaceEnrollPhotoDetect = ApiResponse.ConvertRet(bm, Integer.class);
            	log.info("请求考务人脸识别接口kwClient.faceDetectEnrollPhotoByUrl返回数据{}", resjsonFaceEnrollPhotoDetect);
            	uploadPhoto.setFaceCompareResult(resjsonFaceEnrollPhotoDetect.getData());
            	if (resjsonFaceEnrollPhotoDetect.getCode() == BizStatusCode.KW_SUCCESS_OK.getCode().intValue()
                        && resjsonFaceEnrollPhotoDetect.getData() == 1) {
            		updateCandidateProcess = true;
            	}
        	}
        }
        if(vo.getPhotoType() == 1 && updateCandidateProcess){
        	LambdaQueryWrapper<CandidateProcess> query = Wrappers.lambdaQuery();
            query.eq(CandidateProcess::getCandidateId, vo.getExamCandidateId());
        	CandidateProcess entity = new CandidateProcess();
            entity.setTakePicture(true);
            iCandidateProcessService.update(entity, query);
        }
        return ApiResponse.builder().code(BizStatusCode.KW_SUCCESS_OK.getCode())
                .data(uploadPhoto).build();
    }

    @ApiOperation(value = "7.1.4 获取人脸核身成功后的照片", response = Boolean.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "orderId", value = "订单号", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(4)
    @GetMapping("/getValidPhoto")
    public ApiResponse getValidPhoto(@RequestParam String examId, @RequestParam String examCandidateId,
                                      @RequestParam String admissionNumber, @RequestParam String orderId) {
        try {
            // 放MQ，后续处理
            KsQueueHandleExecutor.getQueue().add(Pair.of(ValidPhotoVO.builder()
                    .examId(examId)
                    .examCandidateId(examCandidateId)
                    .admissionNumber(admissionNumber)
                    .orderId(orderId)
                    .build(),f -> iPcMonitorService.sendMessageAndLog((ValidPhotoVO) f)));
        } catch (Exception e) {
            log.error("{}", e);
        }
        return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).build();
    }

    @ApiOperation(value = "7.1.5 app下载二维码", response = AppQRCodeVO.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "candidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(5)
    @GetMapping("/getAppQRCode")
    public ApiResponse getAppQRCode(@RequestParam String examId, @RequestParam String candidateId) {
        ExamDTO examDTO = examInfoCacheService.getExamInfoFromCache(examId);
        if(Objects.nonNull(examDTO)){
            CandidateProcess candidateProcess = iCandidateProcessService.getOne(Wrappers.<CandidateProcess>lambdaQuery()
                    .eq(CandidateProcess::getExamId, examId).eq(CandidateProcess::getCandidateId, candidateId));
            if(candidateProcess == null) {
                return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).build();
            } else {

                String qrcode = configManager.getWxDefaultQRCode();
                /*String key = KeyDefineCommon.getWxQrCodeKey(examId, candidateId);
                if(redisTemplate.hasKey(key)) {
                    qrcode = (String) redisTemplate.opsForValue().get(key);
                } else {
                    // 缓存没有就从第三方拿，然后放缓存
                    String s = tpsClient.getQRCode(res.getData().getExam().getOfficeId(),
                            res.getData().getExam().getRandomId() + "#" + candidateProcess.getAdmissionNumber(),
                            configManager.getWxDefaultQRCodePage(), configManager.getWxDefaultQRCodeWidth());
                    Result result = JSON.parseObject(s, Result.class);
                    if (result.isSuccess()) {
                        qrcode = configManager.getNginxPath() + result.getObj().toString();
                        redisTemplate.opsForValue().set(key, qrcode, 1, TimeUnit.DAYS);
                    }
                }*/
                AppQRCodeVO data = AppQRCodeVO.builder().androidQRCode(configManager.getAndroidQRCodeUrl())
                        .iosQRCode(configManager.getIosQRCodeUrl()).wxQRCode(qrcode)
                        .randomId(examDTO.getExam().getRandomId()).build();
                return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode())
                        .data(data).build();
            }
        }else{
            return ApiResponse.builder().code(BizStatusCode.GET_EXAM_INFO_FAIL.getCode()).build();
        }
    }

    @ApiOperation(value = "7.1.6 双监控状态，0表示未验证，1表示验证通过，2表示验证不通过", response = Integer.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(6)
    @GetMapping("/getDualMonitoringStates")
    public ApiResponse getDualMonitoringStates(@RequestParam String examCandidateId) {
        String json = kwClient.getDualMonitoringStates(examCandidateId);
        return ApiResponse.ConvertRet(json, String.class);
    }

    @ApiOperation(value = "7.1.7 获取移动端直播视频", response = AppPlayAddressVO.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examCandidateId", value = "考生Id", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(7)
    @GetMapping("getAppPlayAddress")
    public Object getAppPlayAddress(@RequestParam String examCandidateId) {
        String json = kwClient.getAppPlayAddress(examCandidateId);
        log.info("请求考务接口获取移动端直播视频返回数据{}", json);
        ApiResponse apiResponse = JSON.parseObject(json, ApiResponse.class);
        Object data = null;
        if (apiResponse.getCode().intValue() == BizStatusCode.KW_SUCCESS_OK.getCode().intValue()) {
            data = apiResponse.getData();
        }
        return ApiResponse.builder().code(BizStatusCode.KS_SUCCESS_OK.getCode()).data(data).build();
    }

    @ApiOperation(value = "7.1.8 保存截屏", response = AppQRCodeVO.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examId", value = "考试ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "candidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "screenShotUrl", value = "截屏图片地址", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = false),
            @ApiImplicitParam(name = "screenShotDate", value = "截屏时间", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = false),
            @ApiImplicitParam(name = "screenShotState", value = "截屏图片上传状态（1成功，2客户端截屏失败，3上传腾讯云失败）", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = false)
    })
    @ApiOperationSort(8)
    @GetMapping("/saveScreenShot")
    public Object saveScreenShot(@RequestParam String examId, @RequestParam String candidateId, @RequestParam String screenShotUrl, @RequestParam String screenShotDate, @RequestParam Integer screenShotState) {
        String res = kwClient.saveScreenShot(examId, candidateId, screenShotUrl, screenShotDate,screenShotState);
        return res;
    }

    @ApiOperation(value = "7.1.4 pc二次登录人脸验证身", response = Boolean.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "examCandidateId", value = "考生ID", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true),
            @ApiImplicitParam(name = "imageUrl", value = "照片urll", dataType = DataType.STRING,
                    paramType = ParamType.QUERY, required = true)
    })
    @ApiOperationSort(9)
    @GetMapping("/secondValidFace")
    public ApiResponse<Boolean> secondValidFace(@RequestParam String examCandidateId, @RequestParam String imageUrl,
                                                 HttpServletRequest request) {
        try {
            CandidateProcess candidateProcess = iCandidateProcessService.lambdaQuery()
                    .select(CandidateProcess::getExamId, CandidateProcess::getAdmissionNumber,CandidateProcess::getExamStatus)
                    .eq(CandidateProcess::getCandidateId, examCandidateId).one();
            if(Objects.isNull(candidateProcess)){
                return ApiResponse.<Boolean>builder()
                        .code(BizStatusCode.CANDIDATE_NOT_IN_RECORD.getCode())
                        .data(Boolean.FALSE)
                        .build();
            }
            if(candidateProcess.getExamStatus() >= CandidateStatusCode.COMMIT.getCode()){
                return ApiResponse.<Boolean>builder()
                        .code(BizStatusCode.CANDIDATE_FINISH_EXAM_NOT_TO_LOGIN.getCode())
                        .data(Boolean.FALSE)
                        .build();
            }
            Boolean isMatch = iPcMonitorService.secondValidFace(examCandidateId,imageUrl,candidateProcess.getExamId(),request);
            return ApiResponse.<Boolean>builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(isMatch)
                    .build();
        } catch (Exception e) {
            log.error("{pc二次登录人脸验证身发生异常:【】:}", e);
            return ApiResponse.<Boolean>builder()
                    .code(BizStatusCode.KS_SUCCESS_OK.getCode())
                    .data(Boolean.FALSE)
                    .build();
        }
    }
}
